package common.extractText;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.yuntsg.ruiijn.paperana.utils.SmallTool;
import common.util.StringUtil;
import common.util.VerifyUtil;
import lombok.Data;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Tools {

    static String tableName = "pdf.pmid_pdf_sentence"; // 保存的表名
    static String connector = "ㅠ⫅ㅛ".toLowerCase(); // 连接符-
    static String paragraph = "ㅑ╉╧".toLowerCase(); // 段落开始符
    static String subProject = "ㅠ⨦ⱔ".toLowerCase(); // 子段落开始符
    static String lineBreak = "⬆เ⎨".toLowerCase(); // 换行符号，文本加入保存时，前面均要加上这个符号
    static String lastStr = "∱≱⬇".toLowerCase(); // 整个文档的结束符号

    public static int insertTable(Set<String> pOrder, String paragraph_name, List<Integer> sOrder, String str, String subProject_Name, int code, List<Change.TableField> tableList) {
        String sub = subProject_Name.length() > 0 ? subProject_Name : paragraph_name;
        // 如果子段落名称的开头与段落的名称开头不一致，则子段落的名称使用段落的。比如，段落的为：1. Introduction，则子段落的名称必须为类似"1.1. " 这样的，表示是它的子段落。
        sub = (paragraph_name.length() > 1 && sub.length() > 1 && !paragraph_name.substring(0, 1).equals(sub.substring(0, 1))) ? paragraph_name : sub;
        if (str.replace(lastStr, "").trim().length() > 0) {
            sOrder.add(1);
            pOrder.add(paragraph_name);
            Change.TableField tableField = new Change.TableField();
            tableField.setCode(code);
            tableField.setParagraph(pOrder.size());
            tableField.setParagraph_name(paragraph_name);
            tableField.setSentence(sOrder.size());
            tableField.setStr(reStr(str));
            tableField.setSub_project(sub);
            tableList.add(tableField);

//            if (s.contains("have a unique and maximal solution")) {
//                System.out.println("paragraph_name: 0" + s);
//            }

        }
        return sOrder.size();
    }

    // 如果不是参文，如果字符串比较长，再
    public static void forInsert(Set<String> pOrder, String paragraph_name, List<Integer> sOrder, List<String> saveList, String subProject_Name, int code, Set<String> mostStrSet, List<Change.TableField> tableList) {
        saveList = Change.doInsert(saveList, mostStrSet); // 进入程序先进行一次是否合并
        for (String str : saveList) {
            // 对于长度大于560个字符的句子，且不是参文，再次拆分，分别以数字加点加空格加大写字母，或者分号加空格加大写字母，这两个在原来是不拆分的，现在字符串比较长才执行。
            if (str.length() > 560 && !paragraph_name.toLowerCase().contains("references")) {
                List<String> resultList = new ArrayList<>();
                resultList.add(str);
                resultList = Change.forMatcher("[0-9]\\. [A-Z⬆]", 1, 1, resultList);
                resultList = Change.forMatcher("; [A-Z⬆]", 1, 1, resultList);
                resultList = Change.doInsert(resultList, mostStrSet); // 对于大于560的，再作一次是否合并
                for (String s : resultList) {
                    insertTable(pOrder, paragraph_name, sOrder, s, subProject_Name, code, tableList);
                }
            } else {
                insertTable(pOrder, paragraph_name, sOrder, str, subProject_Name, code, tableList);
            }
        }
    }

    // 删除标记符号
    public static String reLabel(String str) {
        return str.replaceAll(" +", " ").replace("- " + lineBreak, "").replace(connector, "").replace(paragraph, "").replace(subProject, "").replace(lineBreak, "").replace(lastStr, "");
    }

    public static String reStr(String str) {
        str = str.replaceAll(" +", " ").replace(lineBreak, "");
        if (Pattern.compile("- ⬆เ⎨[0-9]").matcher(str).find()) { // 如果换行符"⬆เ⎨"前是一个连接符，后面是一个数字，则有可能是页码，仍保留为连接符，若不是则可能是单词的换行，直接给予删除。
            str = str.replace("- ⬆เ⎨", "-");
        }
        str = str.startsWith(" ") ? str.substring(1) : str;
        return reLabel(str);
    }

    // 返回Integer类型的字段的值，field为字段的名称。
    public static int getIntField(Entity entry, String field) {
        if (null != entry) {
            Integer num = entry.getInt(field);
            if (null != num && num > 0) {
                return num;
            }
        }
        return 0;
    }

    public static String printMemoryStr() {
        String freeMemory = null;
        String totalMemory = null;
        String maxMemory = null;
        try {
            freeMemory = Runtime.getRuntime().freeMemory() / 1024 / 1024 / 1024 + " Gb";
            totalMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024 / 1024 + " Gb";
            maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024 / 1024 + " Gb";
            return " 内存申请：" + maxMemory + " 空余：" + freeMemory + " 获取：" + totalMemory;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    // 返回Byte类型的字段的值，field为字段的名称。
    public static String getByteField(Entity entry, String field) {
        if (null != entry) {
            byte[] bytes = new byte[0];
            try {
                try {
                    byte[] b = entry.getBytes(field);
                    if (null != b && b.length > 0) {
                        bytes = ZipUtil.unGzip(b);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } catch (UtilException e) {
                System.out.println("\033[31;1m" + "--getByteField err-- " + "\033[0m");
            }
            String str = new String(bytes, StandardCharsets.UTF_8);
            if (str.length() > 0) {
                return str.trim();
            }
        }
        return "";
    }

    public static boolean isBrackets(String first, String next) {
        next = "n" + next + "n";
        if (first.contains("(") || next.contains(")")) {
            return isEquals(first, next, "\\(", "\\)");
        }
//        if (next.contains("[") || next.contains("]")) {
//            return isEquals(next, "\\[", "\\]");
//        }
//        if (next.contains("{") || next.contains("}")) {
//            return isEquals(next, "{", "}");
//        }
//        if (next.contains("“") || next.contains("”")) {
//            return isEquals(next, "“", "”");
//        }
//        if (next.contains("‘") || next.contains("’")) {
//            return isEquals(next, "‘", "’");
//        }
//        if (next.contains("<") || next.contains(">")) {
//            return isEquals(next, "<", ">");
//        }
        return false;
    }

    // 如果字符串，包含的左括号与包含的右括号，数量不一致，说明可能不完整，上一句与下一句需要合并，因此返回true。
    public static boolean isEquals(String first, String next, String f_tab, String n_tab) {
        int next_l = next.split(f_tab).length;
        int next_r = next.split(n_tab).length;

        int first_l = first.split(f_tab).length;
        int first_r = first.split(n_tab).length;

        // 如果上一句左括号多，下一句右括号多，则返回true
        return next_l < next_r && first_r < first_l;
    }

    @SneakyThrows
    /**
     * 带有多线程确定线程数进行
     * primaryKey 主键 如果不填默认id
     * tableName 表名
     * use 数据操作对象
     * tableName thread 线程数
     */
    public static void getAllDbInfo(Db use, String tableName, String primaryKey, Integer thread, Map<String, Integer> pmutYear) {
        if (StringUtils.isEmpty(primaryKey)) {
            SmallTool.printMessError("没有输入主键 默认 id");
            primaryKey = "id";
        }
        if (thread == null) {
            SmallTool.printMessError("没有输入线程 默认 6线程");
            thread = 6;
        }
        List<Entity> res = Collections.synchronizedList(new ArrayList<>());
        Entity entity = use.queryOne("select count(1) as cou from " + tableName);
        if (entity != null) {
            Integer cou = entity.getInt("cou");
            if (cou > 10000) {
                // 进行分页查询
                SmallTool.printMess("数量大于1万分页查询");
                //+++++++++++++++++++++++++++++++++++++++++++++
                int onePageNum = 10000;
                int from = 1;
                int max = 0;
                List<Entity> tableNum = use.query("select count(1) as count from " + tableName);
                int count = tableNum.get(0).getInt("count");
                if (count > 0) {
                    max = use.queryNumber("select max(" + primaryKey + ") from " + tableName).intValue();
                }
                int page = max / onePageNum;
                if (max % onePageNum != 0) {
                    page++;
                }
                List<PageInfo> pageInfos = new ArrayList<>();
                for (int i = 1; i <= page; i++) {
                    int start = (i - 1) * onePageNum + from;
                    int stop = i * onePageNum + from - 1;
                    if (stop > max) {
                        stop = max + onePageNum;
                    }
                    pageInfos.add(new PageInfo(start, stop));
                }
//                List<Entity> finalRes = Collections.synchronizedList(new ArrayList<>(10000000));
                ExecutorService excutor = Executors.newFixedThreadPool(thread);
                final CountDownLatch countDownLatch = new CountDownLatch(pageInfos.size());
                for (PageInfo pageInfo : pageInfos) {
                    excutor.execute(new Task(pageInfo, tableName, primaryKey, use, pmutYear, countDownLatch, page));
                }
                try {
                    countDownLatch.await();
                } catch (Exception e) {
                } finally {
                    excutor.shutdown();
                }
//                res = finalRes;
                //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            } else {
//                res = use.query("select * from " + tableName);
            }
            if (res.size() == cou) {
                SmallTool.printMess("数量一致 返回++++++++++++");
            } else {
                SmallTool.printMessError("数量不一致 返回++++++++++++");

            }
        }
//        return res;
    }

    // processBuilder.command("/usr/local/bin/mutool", "convert", "-o", txtfile, pdfname); // linux系统下的路径
    public static List<String> pdfToTxt(String directory, String pdfName) throws IOException {
        String name = pdfName.substring(0, pdfName.length() - ".pdf".length()); // 因为不同名，不好找，测试时使用这个。
        String txtFile = name + ".txt";
        ProcessBuilder processBuilder = new ProcessBuilder();
        processBuilder.directory(new File(directory));
        if (VerifyUtil.isServer()) {
            processBuilder.command("/usr/local/bin/mutool", "convert", "-o", txtFile, pdfName);
        } else {
            processBuilder.command("D:\\mutool\\mutool", "convert", "-o", txtFile, pdfName);
        }
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();
        InputStream inputStream = process.getInputStream();
        IoUtil.read(inputStream, StandardCharsets.UTF_8);
        inputStream.close();
//        Files.deleteIfExists(Paths.get(txtFile)); // 将中间的txt文档给予删除
        List<String> strings = FileUtil.readLines(txtFile, "utf-8");
        List<String> resBack = new ArrayList<>();
        for (String string : strings) {
            if (StringUtil.isNotNull(string)) {
                resBack.add(string);
            }
        }
        return resBack;
    }

    public static void dellTxt(String path) throws IOException {
        Map<String, Set<String>> fileMap = new HashMap<>(); // Map的键是目录，值是该目录下的文件列表。
        getFileList(path, fileMap);
        for (Map.Entry<String, Set<String>> entry : fileMap.entrySet()) {
            for (String s : entry.getValue()) {
                if (s.toLowerCase().endsWith(".txt")) {
                    Files.deleteIfExists(Paths.get(s));
                }
            }
        }
    }

    // 递归获取某路径下的所有文件，文件夹
    public static void getFileList(String path, Map<String, Set<String>> fileMap) {
        File file = new File(path);
        String directory = path;
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (null != files) {
                for (File folder : files) {
                    if (folder.isDirectory()) {
                        directory = folder.getPath();
                        getFileList(folder.getPath(), fileMap);
                    } else {
                        putStrSetStr(directory, folder.getPath(), fileMap);
                    }
                }
            }
        } else {
            putStrSetStr(directory, file.getPath(), fileMap);
        }
    }

    public static void putStrSetStr(String name, String str, Map<String, Set<String>> strSetStrMap) {
        if (name.length() > 0 && str.length() > 0) {
            strSetStrMap.compute(name, (k, v) -> {
                if (v == null) {
                    v = new ConcurrentHashSet<>();
                }
                v.add(str);
                return v;
            });
        }
    }

    public static String getStrField(Entity entry, String field) {
        if (null != entry) {
            String str = entry.getStr(field);
            if (null != str && str.length() > 0 && !str.toLowerCase().trim().equals("null")) {
                return str.trim();
            }
        }
        return "";
    }

    // 取小数点后4位
    public static double takeFourDigits(Double d) {
        if (d == null) {
            return 0.0;
        }
        BigDecimal bd = new BigDecimal(d);
        BigDecimal bd2 = bd.setScale(2, RoundingMode.HALF_UP);
        return Double.parseDouble(bd2.toString());
    }

    //reLabel(String str)
    public static Change.TableCheck check(List<Change.TableField> tableList, List<String> resultList) {
        int table = -5;
        int result = 0;
        Set<String> tableSet = new HashSet<>();
        for (String str : resultList) {
            str = reCheck(str);
            String[] split = str.split(" ");
            Collections.addAll(tableSet, split);
            result += str.replaceAll(" +", "").replace(".", "").length();
        }
        for (int i = 0; i < tableList.size(); i++) {
            Change.TableField tableField = tableList.get(i);
            String str = reCheck(tableField.getStr());
            String paragraph_name = reCheck(tableField.getParagraph_name());
            int pn = paragraph_name.replaceAll(" +", "").replace(".", "").length();
            String sub_project = reCheck(tableField.getSub_project());
            int sub = sub_project.replaceAll(" +", "").replace(".", "").length();
            table += str.replaceAll(" +", "").replace(".", "").length();
            removeCheck(paragraph_name, tableSet);
            removeCheck(str, tableSet);
            removeCheck(sub_project, tableSet);
            boolean bool = tableList.size() > i + 1;
            Change.TableField nextTableField = bool ? tableList.get(i + 1) : new Change.TableField();
            String nextParagraph_name = bool ? reCheck(nextTableField.getParagraph_name()) : "";
            String nextSub_project = bool ? reCheck(nextTableField.getSub_project()) : "";
            if (paragraph_name.equals(sub_project)) { // 如果段落与小段落相同
                if (!paragraph_name.equals(nextParagraph_name)) { // 如果段落与下一个段落不相同
                    table += pn;
                }
            } else {
                if (!paragraph_name.equals(nextParagraph_name) && !sub_project.equals(nextSub_project)) { // 如果两者均不相同
                    table += pn + sub;
                } else if (paragraph_name.equals(nextParagraph_name) && !sub_project.equals(nextSub_project)) { // 如果段落与下一个段落相同，但小段落与下一个小段落不相同
                    table += sub;
                } else if (!paragraph_name.equals(nextParagraph_name)) { // 如果段落与下一个段落不相同
                    table += pn;
                }
            }
        }
//            System.out.println("****result: " + result + " table: " + table + " 两者相差： " + (result - table));
        for (String s : tableSet) {
//            System.out.println("缺失： " + s);
        }

        Change.TableCheck tableCheck = new Change.TableCheck();
        tableCheck.setDiffer(result - table);
        tableCheck.setRatio(takeFourDigits(Arith.div((result - table), result == 0 ? 1 : result) * 100));
        tableCheck.setStrNum(result);
        return tableCheck;
    }

    public static void removeCheck(String paragraph_name, Set<String> tableSet) {
        String[] split = paragraph_name.split(" ");
        for (String s : split) {
            tableSet.remove(s);
        }
    }

    public static String reCheck(String str) {
        return str.replaceAll(" +", " ").replace("-", "").replace(connector, "").replace(paragraph, "")
                .replace(subProject, "").replace(lineBreak, "").replace(lastStr, "").toLowerCase();
    }

    public static Map<String, Integer> strMapSortValueInt(Map<String, Integer> mapName, int limit) {
        Map<String, Integer> result = new LinkedHashMap<>();
        mapName.entrySet().stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue().reversed()).limit(limit)
                .forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
        return result;
    }

    /**
     * 各种类型的数据解析：位数的限定，WOS为15位，EJ EB CJ CB 四种 8-9 位的数字，PM 1-8位且小于 50000000 的数字，GG GGSX 为9位数字，除了WOS外，文献ID号均不得以 0 开头，各种类型的员工代码只能为一个字母，不能是数字 。
     * PM 1 ; UT 2 ; GG 3 ; EB 4 ; EJ 5 ; CB 6 ; CJ 7
     */
    public static String getFileId(String str) {
        if (null == str) {
            return "";
        }
        str = str.replaceAll("\\[+", "[").replaceAll("]+", "]");
        // https://utswmed-ir.tdl.org/bitstream/handle/2152.5/5772/PWS%20Paper%20Supplemental%20Tables[2].pdf?sequence=1|Oligonucleotide Primer Sequences Source ir tdl org SO 2018[GGT915422624].pdf
        Matcher matcher = Pattern.compile("\\[([0-9]+)\\]").matcher(str);
        if (matcher.find()) {
            str = str.replace(matcher.group(0), "");
        }
        str = str.replaceAll(" +", "").toUpperCase().replace("[PMID[PMID", "[PMID").replace("].PDF.PDF", "].PDF").replace("].PDF(1).PDF", "].PDF").replace("].PDF(2).PDF", "].PDF").replace("].PDF(3).PDF", "].PDF");
        if (str.endsWith(".PDF")) {
            int pmStart = str.indexOf("[PMID");
            if (pmStart != -1) {
                int pmEnd = str.toUpperCase().indexOf("].PDF");
                if (pmEnd != -1 && (pmEnd - pmStart) > 6 && NumberUtil.isInteger(str.substring(pmStart + 6, pmEnd))) {
                    int id = Integer.parseInt(str.substring(pmStart + 6, pmEnd));
                    if (id < 50000000 && !str.substring(pmStart + 6).startsWith("0")) {
                        if (str.substring(pmStart + 5, pmStart + 6).matches("[a-zA-Z]+")) {
                            return "PM" + str.substring(pmStart + 6, pmEnd);
                        }
                    }
                }
            }
            int utStart = str.indexOf("[WOS");
            if (utStart != -1) {
                int utEnd = str.toUpperCase().indexOf("].PDF");
                if (utEnd != -1 && (utEnd - utStart) > 5 && str.toUpperCase().substring(utStart + 5, utEnd).length() == 15) {
                    if (str.substring(utStart + 4, utStart + 5).matches("[a-zA-Z]+")) {
                        return "UT" + str.substring(utStart + 5, utEnd);
                    }
                }
            }
            int ggStart = str.indexOf("[GG");
            if (ggStart != -1) {
                int ggEnd = str.toUpperCase().indexOf("].PDF");
                if (ggEnd != -1 && (ggEnd - ggStart) > 4 && NumberUtil.isInteger(str.substring(ggStart + 4, ggEnd))) {
                    int id = Integer.parseInt(str.substring(ggStart + 4, ggEnd));
                    if (id > 900000000 && id < 1000000000 && !str.substring(ggStart + 4).startsWith("0")) {
                        if (str.substring(ggStart + 3, ggStart + 4).matches("[a-zA-Z]+")) {
                            return "GG" + str.substring(ggStart + 4, ggEnd);
                        }
                    }
                }
            }
            int ggsxStart = str.indexOf("[GGSX");
            if (ggsxStart != -1) {
                int ggsxEnd = str.toUpperCase().indexOf("].PDF");
                if (ggsxEnd != -1 && (ggsxEnd - ggsxStart) > 6 && NumberUtil.isInteger(str.substring(ggsxStart + 6, ggsxEnd))) {
                    int id = Integer.parseInt(str.substring(ggsxStart + 6, ggsxEnd));
                    if (id > 900000000 && id < 1000000000 && !str.substring(ggsxStart + 6).startsWith("0")) {
                        if (str.substring(ggsxStart + 5, ggsxStart + 6).matches("[a-zA-Z]+")) {
                            return "GG" + str.substring(ggsxStart + 6, ggsxEnd);
                        }
                    }
                }
            }
            int cbStart = str.indexOf("[CBID");
            if (cbStart != -1) {
                int cbEnd = str.toUpperCase().indexOf("].PDF");
                if (cbEnd != -1 && (cbEnd - cbStart) > 6 && NumberUtil.isInteger(str.substring(cbStart + 6, cbEnd))) {
                    int id = Integer.parseInt(str.substring(cbStart + 6, cbEnd));
                    if (id > 10000000 && id < 999999999 && !str.substring(cbStart + 6).startsWith("0")) {
                        if (str.substring(cbStart + 5, cbStart + 6).matches("[a-zA-Z]+")) {
                            return "CB" + str.substring(cbStart + 6, cbEnd);
                        }
                    }
                }
            }
            int cjStart = str.indexOf("[CJID");
            if (cjStart != -1) {
                int cjEnd = str.toUpperCase().indexOf("].PDF");
                if (cjEnd != -1 && (cjEnd - cjStart) > 6 && NumberUtil.isInteger(str.substring(cjStart + 6, cjEnd))) {
                    int id = Integer.parseInt(str.substring(cjStart + 6, cjEnd));
                    if (id > 10000000 && id < 999999999 && !str.substring(cjStart + 6).startsWith("0")) {
                        if (str.substring(cjStart + 5, cjStart + 6).matches("[a-zA-Z]+")) {
                            return "CJ" + str.substring(cjStart + 6, cjEnd);
                        }
                    }
                }
            }
            int ebStart = str.indexOf("[EBID");
            if (ebStart != -1) {
                int ebEnd = str.toUpperCase().indexOf("].PDF");
                if (ebEnd != -1 && (ebEnd - ebStart) > 6 && NumberUtil.isInteger(str.substring(ebStart + 6, ebEnd))) {
                    int id = Integer.parseInt(str.substring(ebStart + 6, ebEnd));
                    if (id > 10000000 && id < 999999999 && !str.substring(ebStart + 6).startsWith("0")) {
                        if (str.substring(ebStart + 5, ebStart + 6).matches("[a-zA-Z]+")) {
                            return "EB" + str.substring(ebStart + 6, ebEnd);
                        }
                    }
                }
            }
            int ejStart = str.indexOf("[EJID");
            if (ejStart != -1) {
                int ejEnd = str.toUpperCase().indexOf("].PDF");
                if (ejEnd != -1 && (ejEnd - ejStart) > 6 && NumberUtil.isInteger(str.substring(ejStart + 6, ejEnd))) {
                    int id = Integer.parseInt(str.substring(ejStart + 6, ejEnd));
                    if (id > 10000000 && id < 999999999 && !str.substring(ejStart + 6).startsWith("0")) {
                        if (str.substring(ejStart + 5, ejStart + 6).matches("[a-zA-Z]+")) {
                            return "EJ" + str.substring(ejStart + 6, ejEnd);
                        }
                    }
                }
            }
            if (str.length() == 12) {
                int bookEnd = str.indexOf(".PDF");
                if (bookEnd != -1 && NumberUtil.isInteger(str.substring(0, bookEnd))) {
                    int id = Integer.parseInt(str.substring(0, bookEnd));
                    if (id > 10000000 && id < 99999999 && !str.startsWith("0")) {
                        return "EB" + str.substring(0, bookEnd);
                    }
                }
            }
        }
        return "";
    }

    // 修改乱字符  java.sql.SQLException: Incorrect string value: '\xF0\x9D\x99\xB8 b...' for column 'figure' at row 1
    public static String emojiConvert(String str) {
        StringBuffer sb = new StringBuffer();
        String patternString = "([\\x{10000}-\\x{10ffff}\ud800-\udfff])";
        try {
            Pattern pattern = Pattern.compile(patternString);
            Matcher matcher = pattern.matcher(str);
            while (matcher.find()) {
                try {
                    matcher.appendReplacement(sb, "[[" + URLEncoder.encode(matcher.group(1), "UTF-8") + "]]");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
            matcher.appendTail(sb);
        } catch (Exception e) {
            System.out.println("出错的STR:" + str);
        }
        return sb.toString();
    }

    @Data
    public static class PageInfo {
        Integer from;
        Integer to;

        public PageInfo() {
        }

        public PageInfo(Integer from, Integer to) {
            this.from = from;
            this.to = to;
        }
    }

    static class Task implements Runnable {

        private PageInfo pageInfo;
        private Integer allSize;
        private String table;
        private String primaryKey;
        private Db use;
        private Map<String, Integer> finalRes;
        private CountDownLatch countDownLatch;

        public Task(PageInfo pageInfo, String table, String primaryKey, Db use, Map<String, Integer> finalRes, CountDownLatch countDownLatch, Integer allSize) {
            this.pageInfo = pageInfo;
            this.table = table;
            this.use = use;
            this.primaryKey = primaryKey;
            this.allSize = allSize;
            this.finalRes = finalRes;
            this.countDownLatch = countDownLatch;
        }


        @Override
        public void run() {
            try {
                List<Entity> tableData = null;

                try {
                    tableData = use.query("select * from " + table + " where " + primaryKey + "  between " + pageInfo.getFrom() + " and " + pageInfo.getTo());
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
                for (Entity tableDatum : tableData) {
                    String str = tableDatum.getStr("pmut");
                    Integer year = tableDatum.getInt("year");
                    if (StringUtil.isNotNull(str) && StringUtil.isNotNull(year) && year.intValue() > 0) {
                        finalRes.put(str, year);
                    }
                }
            } catch (Exception e) {
                SmallTool.printMessError("没有获取到数据++++");
            } finally {
                countDownLatch.countDown();
                SmallTool.printMess(" 现在开始： " + pageInfo.getFrom() + " -- " + pageInfo.getTo() + " 总页码数：" + allSize + "++当前页码++" + (allSize - countDownLatch.getCount()) + " 表名： " + table);

            }

        }
    }

    // 相对精确的加减乘除
    public static class Arith {
        //默认除法运算精度
        private static final int DEF_DIV_SCALE = 6;

        //构造器私有化，让这个类不能实例化
        private Arith() {
        }

        //提供精确的加法运算
        public static double add(double v1, double v2) {
            BigDecimal b1 = BigDecimal.valueOf(v1);
            BigDecimal b2 = BigDecimal.valueOf(v2);
            return b1.add(b2).doubleValue();
        }

        //精确的减法运算
        public static double sub(double v1, double v2) {
            BigDecimal b1 = BigDecimal.valueOf(v1);
            BigDecimal b2 = BigDecimal.valueOf(v2);
            return b1.subtract(b2).doubleValue();
        }

        //精确的乘法运算
        public static double mul(double v1, double v2) {
            BigDecimal b1 = BigDecimal.valueOf(v1);
            BigDecimal b2 = BigDecimal.valueOf(v2);
            return b1.multiply(b2).doubleValue();
        }

        //提供（相对）精确的除法运算，当发生除不尽的情况时，精确到小数点后10位的数字四舍五入
        public static double div(double v1, double v2) {
            BigDecimal b1 = BigDecimal.valueOf(v1);
            BigDecimal b2 = BigDecimal.valueOf(v2);
            return b1.divide(b2, DEF_DIV_SCALE, BigDecimal.ROUND_HALF_UP).doubleValue();
        }
    }


// end
}
